package org.frank.blockchain.p2p.client;

import cn.hutool.json.JSONUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.SneakyThrows;
import org.frank.blockchain.domain.Command;
import org.frank.blockchain.util.Constants;
import org.frank.blockchain.util.InvokeUtil;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.LockSupport;

public class P2pClientHandler extends ChannelInboundHandlerAdapter {

    Map<String, Command> responseMap = new ConcurrentHashMap<String, Command>();
    Map<String, Thread> lockMap = new ConcurrentHashMap<String, Thread>();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Command command = (Command) msg;
        switch (command.getType()) {
            case Request:
                Command response = InvokeUtil.invoke(command);
                String json = JSONUtil.toJsonStr(response);
                json += Constants.END_CHAR;
                ctx.channel().writeAndFlush(Unpooled.wrappedBuffer(json.getBytes()));
                break;
            case Response:
                responseMap.put(command.getRequestId(), command);
                Thread locker = lockMap.get(command.getRequestId());
                LockSupport.unpark(locker);
                lockMap.remove(command.getRequestId());
                break;
            default:
                break;
        }
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @SneakyThrows
    public Command getResponse(String requestId) {
        Thread locker = Thread.currentThread();
        lockMap.put(requestId, locker);
        LockSupport.park(locker);
        Command response = null;
        if (responseMap.containsKey(requestId)) {
            response = responseMap.get(requestId);
            responseMap.remove(requestId);
        }
        return response;
    }
}
